
#include "stdafx.h"
#include "GdiCapture.h"


void SaveBitmap(char *szFilename,HBITMAP hBitmap)
{
	HDC					hdc=NULL;
	FILE*				fp=NULL;
	LPVOID				pBuf=NULL;
	BITMAPINFO			bmpInfo;
	BITMAPFILEHEADER	bmpFileHeader;

	do{

		hdc=GetDC(NULL);
		ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
		bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		GetDIBits(hdc,hBitmap,0,0,NULL,&bmpInfo,DIB_RGB_COLORS);

		if(bmpInfo.bmiHeader.biSizeImage<=0)
			bmpInfo.bmiHeader.biSizeImage=bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount+7)/8;

		if((pBuf=malloc(bmpInfo.bmiHeader.biSizeImage))==NULL)
		{
			MessageBox(NULL,_T("Unable to Allocate Bitmap Memory"),_T("Error"),MB_OK|MB_ICONERROR);
			break;
		}

		bmpInfo.bmiHeader.biCompression=BI_RGB;
		GetDIBits(hdc,hBitmap,0,bmpInfo.bmiHeader.biHeight,pBuf,&bmpInfo,DIB_RGB_COLORS);	

		if((fp=fopen(szFilename,"wb"))==NULL)
		{
			MessageBox(NULL,_T("Unable to Create Bitmap File"),_T("Error"),MB_OK|MB_ICONERROR);
			break;
		}

		bmpFileHeader.bfReserved1=0;
		bmpFileHeader.bfReserved2=0;
		bmpFileHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+bmpInfo.bmiHeader.biSizeImage;
		bmpFileHeader.bfType='MB';
		bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

		fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
		fwrite(&bmpInfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
		fwrite(pBuf,bmpInfo.bmiHeader.biSizeImage,1,fp);

	}while(false);

	if(hdc)
		ReleaseDC(NULL,hdc);

	if(pBuf)
		free(pBuf);

	if(fp)
		fclose(fp);
}

void CaptureScreenshotbyGdi()
{

#define BITSPERPIXEL		32

	BITMAPINFO	bmpInfo;
	ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
	bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	bmpInfo.bmiHeader.biBitCount=BITSPERPIXEL;
	bmpInfo.bmiHeader.biCompression = BI_RGB;
	bmpInfo.bmiHeader.biWidth=GetSystemMetrics(SM_CXSCREEN);
	bmpInfo.bmiHeader.biHeight=GetSystemMetrics(SM_CYSCREEN);
	bmpInfo.bmiHeader.biPlanes=1;
	bmpInfo.bmiHeader.biSizeImage=abs(bmpInfo.bmiHeader.biHeight)*bmpInfo.bmiHeader.biWidth*bmpInfo.bmiHeader.biBitCount/8;

	HWND hDesktopWnd=GetDesktopWindow();
	HDC hDesktopDC=GetDC(hDesktopWnd);
	HDC hDesktopCompatibleDC=CreateCompatibleDC(hDesktopDC);
	LPVOID pBits=NULL;
	HBITMAP hDesktopCompatibleBitmap=CreateDIBSection(hDesktopDC,&bmpInfo,DIB_RGB_COLORS,&pBits,NULL,0);
	if(hDesktopCompatibleDC==NULL || hDesktopCompatibleBitmap == NULL)
	{
		//ErrorMessage("Unable to Create Desktop Compatible DC/Bitmap");	return -1;
	}


	int		nWidth=GetSystemMetrics(SM_CXSCREEN);
	int		nHeight=GetSystemMetrics(SM_CYSCREEN);
	HDC		hBmpFileDC=CreateCompatibleDC(hDesktopDC);
	HBITMAP	hBmpFileBitmap=CreateCompatibleBitmap(hDesktopDC,nWidth,nHeight);
	HBITMAP hOldBitmap = (HBITMAP) SelectObject(hBmpFileDC,hBmpFileBitmap);
	BitBlt(hBmpFileDC,0,0,nWidth,nHeight,hDesktopDC,0,0,SRCCOPY|CAPTUREBLT);
	SelectObject(hBmpFileDC,hOldBitmap);

	SaveBitmap("gdi_screen.bmp",hBmpFileBitmap);

	DeleteDC(hBmpFileDC);
	DeleteObject(hBmpFileBitmap);
	
	if(hDesktopCompatibleDC)
	{
		DeleteDC(hDesktopCompatibleDC);
		hDesktopCompatibleDC = NULL;
	}		
	
	if(hDesktopCompatibleBitmap)
	{
		DeleteObject(hDesktopCompatibleBitmap);
		hDesktopCompatibleBitmap = NULL;
	}

	ReleaseDC(hDesktopWnd, hDesktopDC);
}

namespace Screenshot
{
	GdiCapture::GdiCapture()
		: width(0), height(0)
		, hDesktopWnd(NULL), hDesktopDC(NULL)
		, hDesktopCompatibleDC(NULL)
		, hDesktopCompatibleBitmap(NULL)
		, hBmpFileDC(NULL)
		, hBmpFileBitmap(NULL)
	{

	}
	GdiCapture::~GdiCapture()
	{
		Deinit();
	}

	void GdiCapture::Init()
	{

		width = GetSystemMetrics(SM_CXSCREEN);
		height = GetSystemMetrics(SM_CYSCREEN);

		BITMAPINFO	bmpInfo;
		ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
		bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		bmpInfo.bmiHeader.biBitCount=32;
		bmpInfo.bmiHeader.biCompression = BI_RGB;
		bmpInfo.bmiHeader.biWidth=width;
		bmpInfo.bmiHeader.biHeight=height;
		bmpInfo.bmiHeader.biPlanes=1;
		bmpInfo.bmiHeader.biSizeImage=abs(bmpInfo.bmiHeader.biHeight)*bmpInfo.bmiHeader.biWidth*bmpInfo.bmiHeader.biBitCount/8;

		hDesktopWnd=GetDesktopWindow();
		hDesktopDC=GetDC(hDesktopWnd);
		hDesktopCompatibleDC=CreateCompatibleDC(hDesktopDC);
		LPVOID pBits=NULL;
		hDesktopCompatibleBitmap=CreateDIBSection(hDesktopDC,&bmpInfo,DIB_RGB_COLORS,&pBits,NULL,0);
		if(hDesktopCompatibleDC==NULL || hDesktopCompatibleBitmap == NULL)
		{
			//ErrorMessage("Unable to Create Desktop Compatible DC/Bitmap");	return -1;
		}

		hBmpFileDC=CreateCompatibleDC(hDesktopDC);
		hBmpFileBitmap=CreateCompatibleBitmap(hDesktopDC, width, height);
	}
	void GdiCapture::Deinit()
	{
		if (NULL != hBmpFileBitmap)
		{
			DeleteObject(hBmpFileBitmap);
			hBmpFileBitmap = NULL;
		}

		if (NULL != hBmpFileDC)
		{
			DeleteDC(hBmpFileDC);
			hBmpFileDC = NULL;
		}
		
		if(hDesktopCompatibleDC)
		{
			DeleteDC(hDesktopCompatibleDC);
			hDesktopCompatibleDC = NULL;
		}		

		if(hDesktopCompatibleBitmap)
		{
			DeleteObject(hDesktopCompatibleBitmap);
			hDesktopCompatibleBitmap = NULL;
		}

		if (NULL != hDesktopDC)
		{
			ReleaseDC(hDesktopWnd, hDesktopDC);
			hDesktopDC = NULL;
		}
	}

	bool GdiCapture::IsInit() const
	{
		return (NULL != hBmpFileBitmap);
	}

#include <WinUser.h>
	void GdiCapture::CaptureFrame()
	{
		HBITMAP hOldBitmap = (HBITMAP) SelectObject(hBmpFileDC,hBmpFileBitmap);
		BitBlt(hBmpFileDC,0,0,width,height,hDesktopDC,0,0,SRCCOPY|CAPTUREBLT);

		CURSORINFO cursor = { sizeof(cursor) };
		::GetCursorInfo(&cursor);
		if (cursor.flags == CURSOR_SHOWING) {
			RECT rcWnd;
			::GetWindowRect(hDesktopWnd, &rcWnd);
			ICONINFO info = {sizeof(info)};
			GetIconInfo(cursor.hCursor, &info);

			DWORD dwVersion = 0; 
			dwVersion = GetVersion();

			// Get the Windows version.
			int osVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));; // For some reason cursor icons in 
			// versions older than Vista are not
			// top-aligned. So we compensate. 
			int offsetX = (osVersion >= 6) ? 0 : 10;
			int offsetY = (osVersion >= 6) ? 0 : 10;        

			CPoint coords(0,0);
			CPoint cursorOffset(cursor.ptScreenPos.x - coords.x - offsetX, cursor.ptScreenPos.y - coords.y - offsetY);

			// Now draw the image of the cursor that we captured during
			// the mouse move. DrawIcon will draw a cursor as well.
			::DrawIcon(hBmpFileDC, cursorOffset.x, cursorOffset.y, (HICON)cursor.hCursor);

			//ICONINFOEX info = { sizeof(info) };
			//::GetIconInfoEx(cursor.hCursor, &info);
			//const int x = cursor.ptScreenPos.x - rcWnd.left - rc.left - info.xHotspot;
			//const int y = cursor.ptScreenPos.y - rcWnd.top  - rc.top  - info.yHotspot;
			//BITMAP bmpCursor = {0};
			//::GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
			//::DrawIconEx(hBmpFileDC, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
			//	0, NULL, DI_NORMAL);
		}
		SelectObject(hBmpFileDC,hOldBitmap);

		SaveBitmap("gdi_screen_class.bmp",hBmpFileBitmap);
	}
}